#coding: utf-8

import sys,re
from optparse import OptionParser

OOPS=["add", "sub", "mul", "div", "god", "jpo", "gto", "pop", "pri", "red", "hal"]
TOPS=OOPS+["tod", "tto"]
RE_NUM=re.compile('[-+]?\d+\Z')
DFCODE='epro.code'

class CodeError(Exception):
  pass

class Vm:
  def __init__(self, fname=None, usetape=False):
    try:
      if fname:
        try:
          f=open(fname)
        except IOError:
          f=open(fname+'.code')
      else:
        f=open(DFCODE)
    except IOError:
      raise CodeError('cannot open %s or %s.code' % (fname,fname) if fname \
                         else 'cannot open '+DFCODE)

    ops = TOPS if usetape else OOPS
    self.code=[]
    for l in f:
      l=l.strip()
      if RE_NUM.match(l):
        self.code.append(int(l))
      elif l in ops:
        self.code.append(l)
      else:
        raise CodeError("unknown instruction : "+l)
    f.close

    self.stack=[]
    self.pc = self.step = self.max = 0
    if usetape: self.tape={}

  def run(self,sil=False):
    print('*** START ***')
    try:
      while True:
        if not 0<=self.pc<len(self.code):
          raise Exception('cannot fetch next instruction')
        i=self.code[self.pc]
        if isinstance(i, (int,long)):
          self.con(i)
        elif i=="add":
          self.add()
        elif i=="sub":
          self.sub()
        elif i=="mul":
          self.mul()
        elif i=="div":
          self.div()
        elif i=="god":
          self.god()
        elif i=="jpo":
          self.jpo()
        elif i=="gto":
          self.gto()
        elif i=="pop":
          self.pop()
        elif i=="tod":
          self.tod()
        elif i=="tto":
          self.tto()
        elif i=="pri":
          self.pri()
        elif i=="red":
          self.red(sil)
        elif i=='hal':
          try:
            self.ecode=self.stack.pop()
            break
          except:
            raise Exception('hal: stack is empty')
        self.step+=1
    except:
      print("*** ERROR [%s] *** (%s steps, %s stacks)" % (sys.exc_info()[1],self.step,self.max))
    else:
      if self.ecode==0:
        print("*** HALT *** (%s steps, %s stacks)" % (self.step,self.max))
      else:
        print("*** HALT [%s] *** (%s steps, %s stacks)" % (self.ecode,self.step,self.max))

  def con(self,n):
    if len(self.stack)==self.max:
      self.max+=1
    self.stack.append(n)
    self.pc+=1

  def pop(self):
    try:
      a=self.stack.pop()
    except:
      raise Exception('pop: stack is empty')
    self.pc+=1

  def add(self):
    try:
      y=self.stack.pop()
      x=self.stack.pop()
    except:
      raise Exception('add: stack is empty')
    self.stack.append(x+y)
    self.pc+=1

  def sub(self):
    try:
      y=self.stack.pop()
      x=self.stack.pop()
    except:
      raise Exception('sub: stack is empty')
    self.stack.append(x-y)
    self.pc+=1

  def mul(self):
    try:
      y=self.stack.pop()
      x=self.stack.pop()
    except:
      raise Exception('mul: stack is empty')
    self.stack.append(x*y)
    self.pc+=1

  def div(self):
    try:
      y=self.stack.pop()
      x=self.stack.pop()
    except:
      raise Exception('div: stack is empty')
    try:
      q=x//y
    except:
      raise Exception('div: division by zero')
    if q>=0 or x%y==0:
      self.stack.append(q)
    else:
      self.stack.append(q+1)
    self.pc+=1

  def pri(self):
    try:
      a=self.stack.pop()
    except:
      raise Exception('pri: stack is empty')
    print(a)
    self.pc+=1

  def god(self):
    try:
      n=self.stack.pop()
    except:
      raise Exception('god: stack is empty')
    if not 0<=n<len(self.stack):
      raise Exception('god: stack out of bounds')
    self.stack.append(self.stack[n])
    self.pc+=1

  def gto(self):
    try:
      n=self.stack.pop()
      k=self.stack.pop()
    except:
      raise Exception('gto: stack is empty')
    if not 0<=n<len(self.stack):
      raise Exception('gto: stack out of bounds')
    self.stack[n]=k
    self.pc+=1

  def red(self,sil=False):
    if sil:
      t=sys.stdin.readline()
      if not t:
        raise Exception('end of input')
      t=t.strip()
      if not RE_NUM.match(t):
        raise Exception('input is not an integer! : '+t)
    else:
      while True:
        print('input an integer?')
        t=sys.stdin.readline()
        if not t:
          raise Exception('end of input')
        t=t.strip()
        if RE_NUM.match(t):
          print('OK')
          break
    if len(self.stack)==self.max:
      self.max+=1
    self.stack.append(int(t))
    self.pc+=1

  def jpo(self):
    try:
      n=self.stack.pop()
      k=self.stack.pop()
    except:
      raise Exception('jpo: stack is empty')
    if k>0:
      self.pc=n
    else:
      self.pc+=1

  def tod(self):
    try:
      n=self.stack.pop()
    except:
      raise Exception('tod: stack is empty')
    self.stack.append(self.tape.get(n,0))
    self.pc+=1

  def tto(self):
    try:
      n=self.stack.pop()
      k=self.stack.pop()
    except:
      raise Exception('tto: stack is empty')
    self.tape[n]=k
    self.pc+=1

usage='usage: python %prog [options] [code]'
op=OptionParser(usage)
op.add_option('-s',default=False,action='store_true',dest='sil',help='silent mode')
op.add_option('-t',default=False,action='store_true',dest='tap',help='use tape')
(opts,args)=op.parse_args()

if len(args)==0:
  fname=None
elif len(args)==1:
  fname=args[0]
else:
  op.error('too many arguments')

try:
  Vm(fname, opts.tap).run(opts.sil)
except CodeError,v:
  sys.exit(v)
